﻿Imports System.IO
Imports System.Net
Imports System.Net.Sockets
Imports System.Threading

Module MastermindServer

    ' Server's listener for incomming connections
    Private clientListener As TcpListener
    ' The list of all connected clients
    Private clientList(0, 1) As ConnectedClient

    Sub Main()
        ' First we setup the listener and then we start it to hear for incomming connection calls
        SetupListener()
        StartListening()
    End Sub

    Private Sub SetupListener()
        Dim ip As IPAddress = IPAddress.Any
        Dim port As Integer = 50000
        ' Setup listener, server is accepting calls at any localhost ip in port 50000
        clientListener = New TcpListener(ip, port)
    End Sub

    Private Sub StartListening()
        ' Start listener
        clientListener.Start()
        Console.WriteLine(">> Server is running...")

        While True
            ' Check if there is a client call and accept it
            Console.WriteLine(">> Waiting for a connection...")
            Dim client As TcpClient = clientListener.AcceptTcpClient()

            ' Check if the server is full
            ' If it is, reject the connection
            ' Otherwise accept the connection
            If isFull() Then
                RejectClient(client)
            Else
                AcceptClient(client)
            End If
        End While
    End Sub

    Private Function isFull() As Boolean
        ' Iterate through the client list to see if there is an empty slot
        For i As Integer = 0 To clientList.GetUpperBound(0)
            For j As Integer = 0 To clientList.GetUpperBound(1)
                If clientList(i, j) Is Nothing Then
                    Return False
                End If
            Next
        Next

        Return True
    End Function

    Private Function AddClient(ByVal client As ConnectedClient) As Integer
        Dim role As Integer = 0

        ' Iterate through the client list to find which slot is free
        ' Then add the client to the list
        For i As Integer = 0 To clientList.GetUpperBound(0)
            For j As Integer = 0 To clientList.GetUpperBound(1)
                If clientList(i, j) Is Nothing Then
                    clientList(i, j) = client
                    role = j
                    Return role
                End If
            Next
        Next

        Return role
    End Function

    Private Sub RemoveClient(ByVal client As ConnectedClient)
        ' Iterate through the client list to find which slot contains the client
        ' Then remove the client from the list
        For i As Integer = 0 To clientList.GetUpperBound(0)
            For j As Integer = 0 To clientList.GetUpperBound(1)
                If Not clientList(i, j) Is Nothing AndAlso clientList(i, j).Equals(client) Then
                    clientList(i, j) = Nothing
                    Return
                End If
            Next
        Next
    End Sub

    Private Sub AcceptClient(ByVal client As TcpClient)
        Dim connectedClient = New ConnectedClient(client)
        ' Add client to the list and notify him about his initial role
        Dim role As Integer = AddClient(connectedClient)
        If role = 0 Then
            connectedClient.SendData("CodeMaker")
        ElseIf role = 1 Then
            connectedClient.SendData("CodeBreaker")

            Dim opponent As ConnectedClient = FindOpponent(connectedClient)
            opponent.SendData("Start")
        End If

        Console.WriteLine(">> Accepted a client's connection...")

        ' Start clients reading thread
        StartThread(connectedClient)   
    End Sub

    Private Sub RejectClient(ByVal client As TcpClient)
        Dim rejectedClient As New ConnectedClient(client)
        rejectedClient.SendData("Rejected")
        rejectedClient.ShutDownClient()
        Console.WriteLine(">> Rejected a client's connection...")
    End Sub

    Private Sub StartThread(ByVal client As ConnectedClient)
        Dim thread As New Thread(AddressOf StartReading)
        thread.Start(client)
    End Sub

    Private Sub StartReading(ByVal client As ConnectedClient)
        Dim data As String = ""

        Do
            data = client.ReadData()

            If data <> Nothing Then
                Console.WriteLine(data)

                If data = "Disconnected" OrElse data = "Exit" Then
                    Exit Do
                Else
                    ' Check if client has an opponent
                    Dim opponent As ConnectedClient = FindOpponent(client)

                    ' If he does then send him data
                    If Not opponent Is Nothing Then
                        opponent.SendData(data)
                    End If
                End If
            End If
        Loop While client.isConnected()

        If data = "Disconnected" Then
            ' Check if the disconnected client had any opponent
            Dim hasOpponent As ConnectedClient = FindOpponent(client)

            ' If he did then send him a win message
            If Not hasOpponent Is Nothing Then
                hasOpponent.SendData("You won!")
            End If
        End If
        
        ' Shut down the data stream and remove client from the list
        client.ShutDownClient()
        RemoveClient(client)
    End Sub

    Private Function FindOpponent(ByVal client As ConnectedClient) As ConnectedClient
        Dim opponent As ConnectedClient = Nothing

        ' Iterate through client list to see if a client has an active opponent
        For i As Integer = 0 To clientList.GetUpperBound(0)
            For j As Integer = 0 To clientList.GetUpperBound(1)
                If Not clientList(i, j) Is Nothing AndAlso clientList(i, j).Equals(client) Then
                    If j = 0 Then
                        If Not clientList(i, 1) Is Nothing Then
                            opponent = clientList(i, 1)
                        End If
                    Else
                        If Not clientList(i, 0) Is Nothing Then
                            opponent = clientList(i, 0)
                        End If
                    End If
                End If
            Next
        Next

        Return opponent
    End Function
End Module